/*******************************************************************************
* Copyright (c) 2003-2005, 2013 Till Zoppke.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Till Zoppke - initial API and implementation
******************************************************************************/
/*
* Created on 11.08.2003
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package eniac.window;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import eniac.data.model.parent.Configuration;
import eniac.data.type.EType;
import eniac.data.view.parent.ConfigPanel;
import eniac.skin.Descriptor;
import eniac.skin.Skin;
import eniac.util.EProperties;
import eniac.util.Status;
import eniac.util.StatusListener;
import eniac.util.StringConverter;
/**
* @author zoppke
*
* To change the template for this generated type comment go to Window -
* Preferences - Java - Code Generation - Code and Comments
*/
public class OVPanel extends JPanel implements ChangeListener, Observer, StatusListener {
// rectangle to store the bounds of the xored area
private Rectangle _xorBounds = new Rectangle();
// bufferedImage on which we draw the configuration.
// if we are just moving the xored image, we can unxor at the old position,
// than xor at the new position without refreshing the bufferedimage.
private BufferedImage _bufferImage;
// flag indicating, whether the bufferedImage should be refreshed
private boolean _dirty = true;
/*
* ============================== lifecycle ================================
*/
public OVPanel() {
super(null);
}
public void init() {
// System.out.println("ovinit");
// we don't need a buffer, because our image is already buffered.
setDoubleBuffered(false);
// add ChangeListener to the configurationsPanels viewPort
EFrame.getInstance().addChangeListener(this);
// enable mouse and mouse modtion events
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
// observe configuration
Configuration config = (Configuration) Status.CONFIGURATION.getValue();
if (config != null) {
config.addObserverToTree(this);
}
// add as propertychanglistener to status for get notified
// when skin changes or configuration changes
Status.SKIN.addListener(this);
// add as componentlistener to get notified when we are resized.
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
update((Configuration) Status.CONFIGURATION.getValue(), null);
}
});
}
public void dispose() {
Status.SKIN.removeListener(this);
// TODO: do we need to unregister as observer?
}
/*
* ============================= methods ===================================
*/
@Override
protected void processMouseEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_PRESSED) {
centerConfigurationPanel(e.getX(), e.getY());
}
}
@Override
protected void processMouseMotionEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
centerConfigurationPanel(e.getX(), e.getY());
}
}
// centers the configuration panel
private void centerConfigurationPanel(int x, int y) {
ConfigPanel configPanel = EFrame.getInstance().getConfigPanel();
float dx = (float) configPanel.getWidth() / (float) getWidth();
float dy = (float) configPanel.getHeight() / (float) getHeight();
// determine viewport, init helpers
JViewport viewPort = ((JViewport) configPanel.getParent());
Point p = viewPort.getViewPosition();
// compute x-coordinate
if (viewPort.getWidth() <= configPanel.getWidth()) {
p.x = ((int) (x * dx)) - viewPort.getWidth() / 2;
if (p.x < 0) {
p.x = 0;
}
else if (p.x > configPanel.getWidth() - viewPort.getWidth()) {
p.x = configPanel.getWidth() - viewPort.getWidth();
}
}
// compute y-coordinate
if (viewPort.getHeight() <= configPanel.getHeight()) {
p.y = ((int) (y * dy)) - viewPort.getHeight() / 2;
if (p.y < 0) {
p.y = 0;
}
else if (p.y > configPanel.getHeight() - viewPort.getHeight()) {
p.y = configPanel.getHeight() - viewPort.getHeight();
}
}
// set view position
viewPort.setViewPosition(p);
}
/**
* Returns the preferred Size for this ovPanel. This method will be called
* during initializing the related ovWindow.
*/
public Dimension getPreferredSize() {
// multiply the DV preferred size by the initial ov zoom
int height = StringConverter.toInt(EProperties.getInstance().getProperty("INITIAL_OV_HEIGHT"));
Skin skin = (Skin) Status.SKIN.getValue();
int lod = skin.getLodByHeight(height);
Configuration config = (Configuration) Status.CONFIGURATION.getValue();
// if no configuration, return empty dimension
if (config == null) {
return new Dimension(0, 0);
}
Descriptor descriptor = config.getType().getDescriptor(lod);
// if no descriptor, return empty dimension
if (descriptor == null) {
return new Dimension(0, 0);
}
// compute wanted width by rule of three and return
int width = height * descriptor.getWidth() / descriptor.getHeight();
return new Dimension(width, height);
}
/**
* Paints this ovPanel.
*/
public void paintComponent(Graphics g) {
// if configuration is null, just paint background
if (Status.CONFIGURATION.getValue() == null) {
g.setColor(StringConverter.toColor(EProperties.getInstance().getProperty("BACKGROUND_COLOR")));
g.fillRect(0, 0, getWidth(), getHeight());
return;
}
// compute zoom and appropriate lod
Skin skin = (Skin) Status.SKIN.getValue();
int lod = skin.getLodByHeight(getHeight());
// check that we have a valid lod
if (lod < 0) {
return;
}
// check Image
if (_dirty) {
// create new buffered image
_bufferImage = (BufferedImage) createImage(getWidth(), getHeight());
// determine configurationPanel and paint as icon
ConfigPanel configPanel = EFrame.getInstance().getConfigPanel();
configPanel.paintAsIcon(_bufferImage.getGraphics(), 0, 0, getWidth(), getHeight(), lod);
}
else {
// image is not dirty, so unxor the rectangle
paintXORedRectangle(_bufferImage.getGraphics(), lod);
}
// compute and paint the xored rectangle
// for highlighting the visible area
updateXORedRectangle();
paintXORedRectangle(_bufferImage.getGraphics(), lod);
// paint buffer image to screen
g.drawImage(_bufferImage, 0, 0, getWidth(), getHeight(), this);
// reset dirty flag
_dirty = false;
}
/**
* ChangeListener method. This is called, when the user scrolled the
* detailview frame
*
* @param e
*/
public void stateChanged(ChangeEvent e) {
repaint();
}
private void updateXORedRectangle() {
ConfigPanel configPanel = EFrame.getInstance().getConfigPanel();
Rectangle r = configPanel.getVisibleRect();
float dx = (float) getWidth() / (float) configPanel.getWidth();
float dy = (float) getHeight() / (float) configPanel.getHeight();
_xorBounds.setBounds((int) (r.x * dx), (int) (r.y * dy), (int) (r.width * dx), (int) (r.height * dy));
}
private void paintXORedRectangle(Graphics g, int lod) {
// if image covers the whole dataPanel, we don't need to draw an image
// note: +1 is nice, because for some reason the xorbounds are smaller
// than the panel's width, even when this should not be.
if (_xorBounds.width + 1 >= getWidth() && _xorBounds.height + 1 >= getHeight()) {
return;
}
// get xor image and color
Descriptor d = EType.XOR_IMAGE.getDescriptor(lod);
Image img = (Image) d.get(Descriptor.Key.BACK_IMAGE);
Color color = (Color) d.get(Descriptor.Key.COLOR);
// set xore mode and paint xor image
g.setXORMode(color);
g.drawImage(img, _xorBounds.x, _xorBounds.y, _xorBounds.width, _xorBounds.height, this);
g.setPaintMode();
}
// ============================ listener stuff
// ==============================
/**
* @param o
* @param arg
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void update(Observable o, Object arg) {
// TODO: translate bounds of the calling observable to ovbounds
// and do a partial repaint.
_dirty = true;
repaint();
}
@Override
public void statusChanged(Status status, Object newValue) {
// new skin loaded. Repaint.
update((Configuration) Status.CONFIGURATION.getValue(), null);
}
}